// File: jmcomic-core/src/main/java/dev/jukomu/jmcomic/client/impl/JmApiClient.java
package dev.jukomu.jmcomic.client.impl;

import dev.jukomu.common.postman.Postman;
import dev.jukomu.common.util.json.AdvancedMap;
import dev.jukomu.common.util.json.JsonUtils;
import dev.jukomu.common.util.net.IResponse;
import dev.jukomu.common.util.time.TimeUtils;
import dev.jukomu.jmcomic.client.JmAlbumCommentResp;
import dev.jukomu.jmcomic.client.JmApiResp;
import dev.jukomu.jmcomic.client.JmImageResp;
import dev.jukomu.jmcomic.config.JmMagicConstants;
import dev.jukomu.jmcomic.config.JmModuleConfig;
import dev.jukomu.jmcomic.entity.JmAlbumDetail;
import dev.jukomu.jmcomic.entity.JmFavoritePage;
import dev.jukomu.jmcomic.entity.JmPhotoDetail;
import dev.jukomu.jmcomic.entity.JmSearchPage;
import dev.jukomu.jmcomic.exception.ExceptionUtils;
import dev.jukomu.jmcomic.util.JmApiAdaptUtils;
import dev.jukomu.jmcomic.util.JmCryptoUtils;
import dev.jukomu.jmcomic.util.JmPageUtils;
import dev.jukomu.jmcomic.util.JmcomicText;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 对应Python的JmApiClient，基于禁漫移动端（App）API实现的JmcomicClient。
 */
public class JmApiClient extends AbstractJmClient {

    public static final String CLIENT_KEY = "api";

    // API路径常量
    private static final String API_SEARCH = "/search";
    private static final String API_CATEGORIES_FILTER = "/categories/filter";
    private static final String API_ALBUM = "/album";
    private static final String API_CHAPTER = "/chapter";
    private static final String API_SCRAMBLE = "/chapter_view_template";
    private static final String API_FAVORITE = "/favorite";
    private static final String API_SETTING = "/setting";
    private static final String API_LOGIN = "/login";

    // 线程池用于并发获取Photo详情
    private final ExecutorService photoDetailExecutor = Executors.newCachedThreadPool();

    /**
     * 构造函数。
     *
     * @param postman    Postman实例。
     * @param domainList 域名列表。
     * @param retryTimes 重试次数。
     */
    public JmApiClient(Postman postman, List<String> domainList, int retryTimes) {
        super(postman, domainList, retryTimes);
        afterInit();
    }

    @Override
    public String getClientKey() {
        return CLIENT_KEY;
    }

    @Override
    protected void afterInit() {
        // 自动更新禁漫API域名
        if (JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN) {
            updateApiDomain();
        }
        // 确保拥有Cookies
        if (JmModuleConfig.FLAG_API_CLIENT_REQUIRE_COOKIES) {
            ensureHaveCookies();
        }
    }

    @Override
    public JmSearchPage search(String searchQuery, int page, int mainTag, String orderBy, String time, String category, String subCategory) {
        String cacheKey = String.format("api_search_%s_%d_%d_%s_%s_%s_%s",
                searchQuery, page, mainTag, orderBy, time, category, subCategory);

        return fetchFromCacheOrNetwork(cacheKey, () -> {
            Map<String, Object> params = new HashMap<>();
            params.put("main_tag", mainTag);
            params.put("search_query", searchQuery);
            params.put("page", page);
            params.put("o", orderBy);
            params.put("t", time);

            JmApiResp resp = reqApi(buildUrlWithParams(API_SEARCH, params), true, true, null);
            AdvancedMap data = resp.getModelData();

            // 处理直接搜索ID的重定向情况
            if (data.get("redirect_aid") != null) {
                String aid = (String) data.get("redirect_aid");
                return JmSearchPage.wrapSingleAlbum(getAlbumDetail(aid));
            }

            return JmPageUtils.parseApiToSearchPage(data);
        });
    }

    @Override
    public JmSearchPage categoriesFilter(int page, String time, String category, String orderBy, String subCategory) {
        String o = time.equals(JmMagicConstants.TIME_ALL) ? orderBy : orderBy + "_" + time;
        String cacheKey = String.format("api_categories_%d_%s_%s_%s_%s",
                page, time, category, orderBy,subCategory);
        return fetchFromCacheOrNetwork(cacheKey, () -> {
            Map<String, Object> params = new HashMap<>();
            params.put("page", page);
            params.put("order", ""); // 该参数为空
            params.put("c", category);
            params.put("o", o);

            JmApiResp resp = reqApi(buildUrlWithParams(API_CATEGORIES_FILTER, params), true, true, null);
            return JmPageUtils.parseApiToSearchPage(resp.getModelData());
        });
    }

    @Override
    public JmAlbumDetail getAlbumDetail(String albumId) {
        String validatedId = JmcomicText.parseToJmId(albumId);
        String cacheKey = "api_album_" + validatedId;
        return fetchFromCacheOrNetwork(cacheKey, () -> {
            String url = buildUrlWithParams(API_ALBUM, Collections.singletonMap("id", validatedId));
            JmApiResp resp = reqApi(url, true, true, null);

            if (resp.getResData().get("name") == null) {
                ExceptionUtils.raiseMissing(resp, validatedId);
            }

            return JmApiAdaptUtils.parseEntity(resp.getResData(), JmAlbumDetail.class);
        });
    }

    @Override
    public JmPhotoDetail getPhotoDetail(String photoId, boolean fetchAlbum, boolean fetchScrambleId) {
        String validatedId = JmcomicText.parseToJmId(photoId);
        String cacheKey = "api_photo_" + validatedId;

        CompletableFuture<JmAlbumDetail> albumFuture = fetchAlbum ?
                CompletableFuture.supplyAsync(() -> getAlbumDetail(validatedId), photoDetailExecutor) :
                CompletableFuture.completedFuture(null);

        CompletableFuture<String> scrambleIdFuture = fetchScrambleId ?
                CompletableFuture.supplyAsync(() -> getScrambleId(validatedId, null), photoDetailExecutor) :
                CompletableFuture.completedFuture(null);

        try {
            JmPhotoDetail photo = fetchFromCacheOrNetwork(cacheKey, () -> {
                // 使用CompletableFuture实现并发获取
                CompletableFuture<JmPhotoDetail> photoFuture = CompletableFuture.supplyAsync(() -> {
                    String url = buildUrlWithParams(API_CHAPTER, Collections.singletonMap("id", validatedId));
                    JmApiResp resp = reqApi(url, true, true, null);
                    if (resp.getResData().get("name") == null) {
                        ExceptionUtils.raiseMissing(resp, validatedId);
                    }
                    return JmApiAdaptUtils.parseEntity(resp.getResData(), JmPhotoDetail.class);
                }, photoDetailExecutor);

                return photoFuture.get();
            });

            JmAlbumDetail album = albumFuture.get();
            String scrambleId = scrambleIdFuture.get();

            if (album != null) {
                photo.setFromAlbum(album);
            }
            if (scrambleId != null) {
                photo.setScrambleId(scrambleId);
            }
            return photo;
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException("Failed to fetch photo detail concurrently", e.getCause() != null ? e.getCause() : e);
        }
    }

    @Override
    public void checkPhoto(JmPhotoDetail photo) {
        // 确保fromAlbum存在
        if (photo.getFromAlbum() == null) {
            photo.setFromAlbum(getAlbumDetail(photo.getAlbumId()));
        }

        // **核心修正点**: 检查pageArr是否为空，如果为空则从API获取
        if (photo.getPageArr() == null || photo.getPageArr().isEmpty()) {
            // 调用getPhotoDetail获取完整的章节信息，但只获取photo本身，不递归获取album
            JmPhotoDetail newPhoto = getPhotoDetail(photo.getPhotoId(), false, true);

            // 将获取到的新信息更新到原始photo对象中
            photo.setPageArr(newPhoto.getPageArr());
            photo.setDataOriginalDomain(newPhoto.getDataOriginalDomain());
            photo.setDataOriginal0(newPhoto.getDataOriginal0());
            // scrambleId也可能在getPhotoDetail中被更新，所以同步一下
            if (newPhoto.getScrambleId() != null && !newPhoto.getScrambleId().isEmpty()) {
                photo.setScrambleId(newPhoto.getScrambleId());
            }
        }
    }

    /**
     * 获取章节的Scramble ID。
     *
     * @param photoId 章节ID。
     * @param albumId 本子ID（可选，用于缓存）。
     * @return Scramble ID字符串。
     */
    public String getScrambleId(String photoId, String albumId) {
        String validatedId = JmcomicText.parseToJmId(photoId);

        // 检查缓存
        if (JmModuleConfig.SCRAMBLE_CACHE.containsKey(validatedId)) {
            return JmModuleConfig.SCRAMBLE_CACHE.get(validatedId);
        }
        if (albumId != null && JmModuleConfig.SCRAMBLE_CACHE.containsKey(albumId)) {
            return JmModuleConfig.SCRAMBLE_CACHE.get(albumId);
        }

        // 请求获取
        Map<String, Object> params = new HashMap<>();
        params.put("id", validatedId);
        params.put("mode", "vertical");
        params.put("page", "0");
        params.put("app_img_shunt", "1");
        params.put("express", "off");
        params.put("v", dev.jukomu.common.util.time.TimeUtils.timestamp());

        JmApiResp resp = reqApi(API_SCRAMBLE, true, false, Collections.singletonMap("params", params));

        String scrambleId = dev.jukomu.jmcomic.util.PatternUtils.matchOrDefault(
                resp.getText(), JmcomicText.PATTERN_HTML_ALBUM_SCRAMBLE_ID, null
        );

        if (scrambleId == null) {
            JmModuleConfig.jmLog("api.scramble", "未匹配到scramble_id，使用默认值。响应文本：" + resp.getText());
            scrambleId = String.valueOf(JmMagicConstants.SCRAMBLE_220980);
        }

        // 更新缓存
        JmModuleConfig.SCRAMBLE_CACHE.put(validatedId, scrambleId);
        if (albumId != null) {
            JmModuleConfig.SCRAMBLE_CACHE.put(albumId, scrambleId);
        }

        return scrambleId;
    }

    /**
     * 获取App设置信息。
     *
     * @return JmApiResp响应对象。
     */
    public JmApiResp setting() {
        String cacheKey = "api_setting";

        JmApiResp resp = fetchFromCacheOrNetwork(cacheKey, () -> {
            return reqApi(API_SETTING, true, true, null);
        });

        String settingVer = (String) resp.getModelData().get("version");
        if (settingVer != null && settingVer.compareTo(JmMagicConstants.APP_VERSION) > 0 && JmModuleConfig.FLAG_USE_VERSION_NEWER_IF_BEHIND) {
            JmModuleConfig.jmLog("api.setting", "发现新App版本，更新内置版本号: [" + JmMagicConstants.APP_VERSION + "] -> [" + settingVer + "]");
            JmMagicConstants.APP_VERSION = settingVer;
        }

        return resp;
    }

    @Override
    public IResponse login(String username, String password) {
        Map<String, Object> data = new HashMap<>();
        data.put("username", username);
        data.put("password", password);

        JmApiResp resp = reqApi(API_LOGIN, false, true, Collections.singletonMap("data", data));

        // 更新Cookies
        Map<String, Object> cookies = new HashMap<>();
        if (resp.getRawResponse() instanceof okhttp3.Response) {
            okhttp3.Response rawResp = resp.getRawResponse();
            // OkHttp的CookieJar会自动处理，但我们仍需提取AVS
            String avs = (String) resp.getResData().get("s");
            cookies.put("AVS", avs);
            // 将新Cookie更新到Postman的metaData中
            Map<String, Object> currentCookies = postman.getMetaData("cookies", new HashMap<>());
            currentCookies.putAll(cookies);
            postman.setItem("cookies", currentCookies);
        }

        return resp;
    }

    @Override
    public JmFavoritePage getFavoriteFolder(int page, String orderBy, String folderId, String username) {
        Map<String, Object> params = new HashMap<>();
        params.put("page", page);
        params.put("folder_id", folderId);
        params.put("o", orderBy);

        JmApiResp resp = reqApi(API_FAVORITE, true, true, Collections.singletonMap("params", params));
        return JmPageUtils.parseApiToFavoritePage(resp.getModelData());
    }

    @Override
    public IResponse addFavoriteAlbum(String albumId, String folderId) {
        Map<String, Object> data = new HashMap<>();
        data.put("aid", albumId);

        JmApiResp resp = reqApi(API_FAVORITE, false, true, Collections.singletonMap("data", data));

        // 检查响应状态
        if (!"ok".equals(resp.getModelData().get("status"))) {
            ExceptionUtils.raisesResp((String) resp.getModelData().get("msg"), resp);
        }

        return resp;
    }

    @Override
    public JmAlbumCommentResp albumComment(String videoId, String comment, String originator, String status, String commentId) {
        // API端暂未发现评论接口，抛出不支持操作异常
        throw new UnsupportedOperationException("API client does not support album commenting.");
    }

    @Override
    public JmImageResp downloadImage(String imgUrl, String imgSavePath, String scrambleId, boolean decodeImage) throws IOException {
        JmImageResp resp = getJmImage(imgUrl);
        resp.requireSuccess();
        resp.transferTo(imgSavePath, scrambleId, decodeImage, imgUrl);
        return resp;
    }

    @Override
    public JmImageResp downloadByImageDetail(dev.jukomu.jmcomic.entity.JmImageDetail image, String imgSavePath, boolean decodeImage) throws IOException {
        return downloadImage(image.getDownloadUrl(), imgSavePath, image.getScrambleId(), decodeImage);
    }

    @Override
    public JmImageResp getJmImage(String imgUrl) {
        try {
            IResponse resp = get(imgUrl, new HashMap<>() {{
                put("headers", new HashMap<>(JmModuleConfig.APP_HEADERS_TEMPLATE) {{
                    putAll(JmModuleConfig.APP_HEADERS_IMAGE);
                }});
            }});
            return new JmImageResp(resp.getRawResponse());
        } catch (Exception e) {
            throw new RuntimeException("Failed to get JM image", e);
        }
    }

    /**
     * 自动更新API域名。
     */
    private void updateApiDomain() {
        if (JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE.get()) {
            return;
        }
        if (JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE.compareAndSet(false, true)) {
            try {
                IResponse resp = getRootPostman().get(JmModuleConfig.API_URL_DOMAIN_SERVER);
                String text = resp.getText();
                String decodedJson = JmCryptoUtils.decodeRespData(resp.getText(), "", JmMagicConstants.API_DOMAIN_SERVER_SECRET);
                Map<String, Object> resData = JsonUtils.parseStringAsMap(decodedJson);

                List<String> newServerList = (List<String>) resData.get("Server");
                if (newServerList != null && !newServerList.isEmpty()) {
                    List<String> oldServerList = JmModuleConfig.DOMAIN_API_LIST;
                    JmModuleConfig.jmLog("api.update_domain.success", "获取到最新的API域名，替换jmcomic内置域名：(new)" + newServerList + " ---→ (old)" + oldServerList);
                    if (this.domainList == oldServerList) {
                        this.domainList = newServerList;
                    }
                    JmModuleConfig.DOMAIN_API_LIST = Collections.unmodifiableList(newServerList);
                } else {
                    JmModuleConfig.jmLog("api.update_domain.empty", "获取禁漫最新API域名失败, 返回值: " + decodedJson);
                }
            } catch (Exception e) {
                JmModuleConfig.jmLog("api.update_domain.error", "自动更新API域名失败，仍使用jmcomic内置域名。可通过代码[JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN=False]关闭自动更新API域名. 异常： " + e.getMessage());
            }
        }
    }

    /**
     * 确保客户端拥有Cookies。
     */
    private void ensureHaveCookies() {
        if (postman.getMetaData("cookies") != null) {
            return;
        }
        synchronized (this) {
            if (postman.getMetaData("cookies") != null) {
                return;
            }
            postman.setItem("cookies", getCookies());
        }
    }

    /**
     * 通过调用setting接口获取Cookies。
     *
     * @return Cookies Map。
     */
    private Map<String, Object> getCookies() {
        JmApiResp resp = setting();
        if (resp.getRawResponse() instanceof okhttp3.Response) {
            okhttp3.Response rawResp = resp.getRawResponse();
            // OkHttp的CookieJar会自动处理，这里我们只提取Set-Cookie头中的内容
            // 或依赖于底层的CookieJar。为简单起见，我们假设底层Postman的CookieJar已生效。
            // Python的实现是`dict(resp.resp.cookies)`，这里我们假设OkHttp的CookieJar已自动处理
        }
        return new HashMap<>(); // 返回一个空Map，因为OkHttp的CookieJar会话管理
    }

    /**
     * 请求API的通用方法。
     *
     * @param url            路径。
     * @param isGet          是否是GET请求。
     * @param requireSuccess 是否要求响应成功。
     * @param kwargs         请求参数。
     * @return JmApiResp响应对象。
     */
    private JmApiResp reqApi(String url, boolean isGet, boolean requireSuccess, Map<String, Object> kwargs) {
        long timestamp;
        Map<String, Object> finalKwargs = (kwargs != null) ? new HashMap<>(kwargs) : new HashMap<>();
        Map<String, String> headers = (Map<String, String>) finalKwargs.computeIfAbsent("headers", k -> new HashMap<>());

        if (url.equals(API_SCRAMBLE)) {
            timestamp = dev.jukomu.common.util.time.TimeUtils.timestamp();
            String[] tokens = JmCryptoUtils.tokenAndTokenparam(timestamp, null, JmMagicConstants.APP_TOKEN_SECRET_2);
            headers.put("token", tokens[0]);
            headers.put("tokenparam", tokens[1]);
        } else if (JmModuleConfig.FLAG_USE_FIX_TIMESTAMP) {
            Object[] tsTokens = JmModuleConfig.getFixTsTokenTokenparam();
            timestamp = (long) tsTokens[0];
            headers.put("token", (String) tsTokens[1]);
            headers.put("tokenparam", (String) tsTokens[2]);
        } else {
            timestamp = TimeUtils.timestamp();
            String[] tokens = JmCryptoUtils.tokenAndTokenparam(timestamp, null, null);
            headers.put("token", tokens[0]);
            headers.put("tokenparam", tokens[1]);
        }

        try {
            IResponse resp;
            if (isGet) {
                resp = get(url, finalKwargs);
            } else {
                resp = post(url, finalKwargs);
            }

            JmApiResp apiResp = new JmApiResp(resp.getRawResponse(), timestamp);
            if (requireSuccess) {
                apiResp.requireSuccess();
            }
            return apiResp;
        } catch (Exception e) {
            throw new RuntimeException("API request failed for URL: " + url, e);
        }
    }

    /**
     * 将路径和参数构建为完整的URL。
     */
    private String buildUrlWithParams(String path, Map<String, Object> params) {
        if (params == null || params.isEmpty()) {
            return path;
        }
        String queryString = params.entrySet().stream()
                .map(e -> e.getKey() + "=" + e.getValue())
                .collect(Collectors.joining("&"));
        return path + "?" + queryString;
    }
}